package com.github.data_manage.service

import com.fasterxml.jackson.databind.ObjectMapper
import com.github.data_manage.config.FUND_INTRODUCE_DIR
import com.github.data_manage.config.FUND_MONTH_REPORT_DIR
import com.github.data_manage.dao.FundFileUploadRecordDao
import com.github.data_manage.dao.FundIntroduceFileDao
import com.github.data_manage.dao.FundMonthReportFileDao
import com.github.data_manage.dao.UserMessageDao
import com.github.data_manage.domain.po.Fund
import com.github.data_manage.domain.po.FundFileUploadRecord
import com.github.data_manage.domain.po.FundMeeting
import com.github.data_manage.domain.po.UserMessage
import com.github.data_manage.enum.FundFileType
import com.github.data_manage.util.AuthContextHolder
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import okio.IOException
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.env.Environment
import org.springframework.stereotype.Component
import java.io.File
import java.time.LocalDateTime
import kotlin.collections.mapOf


/**
 *
 * @author chenhainan
 * @since 2025/9/28
 */
@Component
class FundAiUploadService(
    private val okHttpClient: OkHttpClient,
    private val fundFileUploadRecordDao: FundFileUploadRecordDao,
    private val authContextHolder: AuthContextHolder,
    private val userMessageDao: UserMessageDao,
    private val objectMapper: ObjectMapper,
    private val fundIntroduceFileDao: FundIntroduceFileDao,
    private val fundMonthReportFileDao: FundMonthReportFileDao,
    private val environment: Environment
) {

    companion object {

        private val logger = LoggerFactory.getLogger(FundAiUploadService::class.java)
    }


    @Value($$"${system.info-file-upload-url}")
    lateinit var infoFileUploadUrl: String

    @Value($$"${system.before-meeting-file-upload-url}")
    lateinit var beforeMeetingUrl: String

    @Value($$"${system.after-meeting-file-upload-url}")
    lateinit var afterMeetingUrl: String

    /**
     * 上传基金的介绍与月报文件到 ai
     */
    fun uploadFundInfoFilesToAi(
        files: Array<File>,
        fileTypeMap: Map<String, FundFileType>,
//        fundFileType: FundFileType,
        userId: Int = authContextHolder.currentUser().id!!
    ) {
        Thread.startVirtualThread {
            val body: RequestBody = objectMapper.writeValueAsString(
                mapOf(
                    Pair(
                        "pdf_paths",
                        files.map { it.absolutePath }
                    )
                )
            ).toRequestBody("application/json".toMediaType())
            val request = Request.Builder()
                .url(infoFileUploadUrl)
                .post(body)
                .build()

            logger.info("上传基金文件 [{}] 到ai [{}]", files, infoFileUploadUrl)
            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    logger.error("Failed to upload file to Ai", e)
                    for ((fileName, fundFileType) in fileTypeMap) {
                        val userMessage = UserMessage()
                        userMessage.userId = userId
                        userMessage.message = "${fundFileType.message}文件[${fileName}]发送ai解析失败"
                        userMessage.alreadyRead = false
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    }
                }

                override fun onResponse(call: Call, response: Response) {
                    val responseResult = response.body.string()
                    logger.info("ai fund file upload Response code [{}] result: [{}]",response.code,responseResult)

                    if (response.code == 200) {
                        val resultTree = objectMapper.readTree(responseResult)
                        val jobId = resultTree["job_id"].textValue()
                        for (fileNode in resultTree["files_to_process"]) {
                            val fileName = fileNode.textValue()
                            val fundFileUploadRecord = FundFileUploadRecord()
                            fundFileUploadRecord.fileName = fileName
                            val fundFileType = fileTypeMap[fileName]!!
                            fundFileUploadRecord.fileType = fundFileType.name
                            fundFileUploadRecord.userId = userId
                            fundFileUploadRecord.jobId = jobId
                            fundFileUploadRecordDao.save(fundFileUploadRecord)

                            val userMessage = UserMessage()
                            userMessage.userId = userId
                            userMessage.message = "${fundFileType.message}文件[${fileName}]开始解析"
                            userMessage.alreadyRead = false
                            userMessage.fileUploadRecordId = fundFileUploadRecord.id!!
                            userMessage.createTime = LocalDateTime.now()
                            userMessageDao.save(userMessage)
                        }
                    } else {
                        fileTypeMap.forEach { entry ->
                            val fileName = entry.key
                            val fileType = entry.value
                            val userMessage = UserMessage()
                            userMessage.userId = userId
                            userMessage.message = "${fileType.message}文件[${fileName}]开始解析失败"
                            userMessage.alreadyRead = false
                            userMessage.fileUploadRecordId = null
                            userMessage.createTime = LocalDateTime.now()
                            userMessageDao.save(userMessage)
                        }
                    }
                }
            })
        }
    }

    /**
     * 生成会前纪要
     */
    fun beforeMeeting(
        fund: Fund,
        userId: Int = authContextHolder.currentUser().id!!
    ) {
        Thread.startVirtualThread {
            val fundId: Int = fund.fundId!!
//            val files = listOf(
//                fundIntroduceFileDao.findAllByFundId(fundId).map { "${FUND_INTRODUCE_DIR}/${fundId}/${it.fileName}" },
//                fundMonthReportFileDao.findAllByFundId(fundId)
//                    .map { "${FUND_MONTH_REPORT_DIR}/${fundId}/${it.fileName}" },
//            ).flatten().map { File(it) }
            val files = listOf(
                fundIntroduceFileDao.findAllByFundId(fundId)
                    .map { Pair("${FUND_INTRODUCE_DIR}/${fundId}/${it.fileName}", FundFileType.INTRODUCE) },
                fundMonthReportFileDao.findAllByFundId(fundId)
                    .map { Pair("${FUND_MONTH_REPORT_DIR}/${fundId}/${it.fileName}", FundFileType.INTRODUCE) }
            ).flatten()

            val body: RequestBody = objectMapper.writeValueAsString(
                mapOf(
                    Pair(
                        "pdf_paths",
//                        "pdf_files",
                        files.map { File(it.first).absolutePath }
                    )
                )
            ).apply { logger.info("upload beforeMeeting info [{}]", this) }
                .toRequestBody("application/json".toMediaType())
            val request = Request.Builder()
                .url(beforeMeetingUrl)
                .post(body)
                .build()
            logger.info("上传会前纪要文件 [{}] 到ai [{}]", files, beforeMeetingUrl)
            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    logger.error("Failed to upload file to Ai", e)
                }

                override fun onResponse(call: Call, response: Response) {
                    val responseResult = response.body.string()
                    logger.info("before meeting file upload Response code [{}] result: [{}]",response.code,responseResult)

                    if (response.code == 200) {
                        val resultTree = objectMapper.readTree(responseResult)
                        val jobId = resultTree["job_id"].textValue()
                        for (file in files) {
                            val fileName = file.first
                            val fundFileUploadRecord = FundFileUploadRecord()
                            fundFileUploadRecord.fileName = fileName
                            fundFileUploadRecord.fileType = file.second.name
                            fundFileUploadRecord.userId = userId
                            fundFileUploadRecord.jobId = jobId
                            fundFileUploadRecord.fundId = fund.fundId!!
                            fundFileUploadRecordDao.save(fundFileUploadRecord)
                        }


                        val userMessage = UserMessage()
                        userMessage.userId = userId
                        userMessage.message = "上传基金${fund.fundName}会前纪要文件"
                        userMessage.alreadyRead = false
                        userMessage.fileUploadRecordId = 0
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    } else {
                        val userMessage = UserMessage()
                        userMessage.userId = userId
                        userMessage.message = "上传基金${fund.fundName}会前纪要文件失败"
                        userMessage.alreadyRead = false
                        userMessage.fileUploadRecordId = 0
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    }
                }
            })
        }
    }

    /**
     * 生成会后纪要
     */
    fun afterMeeting(
        fund: Fund,
        fundMeeting: FundMeeting,
        userId: Int = authContextHolder.currentUser().id!!
    ) {
        Thread.startVirtualThread {
            val fundId: Int = fund.fundId!!
            val files = listOf(
                fundIntroduceFileDao.findAllByFundId(fundId)
                    .map { Pair("${FUND_INTRODUCE_DIR}/${fundId}/${it.fileName}", FundFileType.INTRODUCE) },
                fundMonthReportFileDao.findAllByFundId(fundId)
                    .map { Pair("${FUND_MONTH_REPORT_DIR}/${fundId}/${it.fileName}", FundFileType.INTRODUCE) }
            ).flatten()

            val body: RequestBody = objectMapper.writeValueAsString(
                mapOf(
                    Pair(
                        "pdf_paths",
                        files.map { File(it.first).absolutePath }
                    ),
                    Pair(
                        "meeting_info",
                        objectMapper.writeValueAsString(
                            mapOf(
                                Pair("meetingTime", fundMeeting.meetingTime),
                                Pair("location", fundMeeting.location),
                                Pair("persons", fundMeeting.persons),
                                Pair("opponentPersons", fundMeeting.opponentPersons),
                                Pair("comments", fundMeeting.comments),
                                Pair("score", fundMeeting.score),
                                Pair("otter", fundMeeting.otter),
                                Pair("meetingDetails", fundMeeting.meetingDetails),
                            )
                        )
                    )
                )
            ).toRequestBody("application/json".toMediaType())

            val request = Request.Builder()
                .url(afterMeetingUrl)
                .post(body)
                .build()
            logger.info("上传会后纪要文件 [{}] 到ai [{}]", files,afterMeetingUrl)
            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    logger.error("Failed to upload file to Ai", e)
                }

                override fun onResponse(call: Call, response: Response) {
                    val responseResult = response.body.string()
                    logger.info("after meeting file upload Response code [{}] result: [{}]",response.code,responseResult)

                    if (response.code == 200) {
                        val resultTree = objectMapper.readTree(responseResult)
                        val jobId = resultTree["job_id"].textValue()
                        for (file in files) {
                            val fileName = file.first
                            val fundFileUploadRecord = FundFileUploadRecord()
                            fundFileUploadRecord.fileName = fileName
                            fundFileUploadRecord.fileType = file.second.name
                            fundFileUploadRecord.userId = userId
                            fundFileUploadRecord.jobId = jobId
                            fundFileUploadRecord.fundId = fund.fundId!!
                            fundFileUploadRecord.fundMeetingId = fundMeeting.id!!
                            fundFileUploadRecordDao.save(fundFileUploadRecord)
                        }


                        val userMessage = UserMessage()
                        userMessage.userId = userId
                        userMessage.message = "上传基金${fund.fundName}会后纪要文件"
                        userMessage.alreadyRead = false
                        userMessage.fileUploadRecordId = 0
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    } else {
                        val userMessage = UserMessage()
                        userMessage.userId = userId
                        userMessage.message = "上传基金${fund.fundName}会后纪要文件失败"
                        userMessage.alreadyRead = false
                        userMessage.fileUploadRecordId = 0
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    }
                }
            })
        }
    }
}